home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NeXTSTEP 3.3 (Developer)…68k, x86, SPARC, PA-RISC]
/
NeXTSTEP 3.3 Dev Intel.iso
/
NextDeveloper
/
Source
/
GNU
/
cctools
/
as
/
i860.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-06
|
29KB
|
1,235 lines
/* i860.c -- Assemble for the i860
Copyright (C) 1989 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <mach-o/i860/reloc.h>
#include "i860-opcode.h"
#include "as.h"
#include "flonum.h"
#include "expr.h"
#include "hash.h"
#include "frags.h"
#include "fixes.h"
#include "read.h"
#include "md.h"
#include "symbols.h"
#include "messages.h"
#include "sections.h"
/*
* These are the default cputype and cpusubtype for the i860 architecture.
*/
const cpu_type_t md_cputype = CPU_TYPE_I860;
cpu_subtype_t md_cpusubtype = CPU_SUBTYPE_I860_ALL;
/*
* This is the byte sex for the i860 architecture. The chip is running in
* big endian mode so the assembler puts out the entire file (instructions
* included) in big endian. When the program is loaded in memory to be run
* the program doing the loading byte swaps the fix width instructions. If
* this is not to be done by the loading program then BYTE_SWAP can be defined
* in here that will put out the instructiona in little endian in the object
* file.
*/
const enum byte_sex md_target_byte_sex = BIG_ENDIAN_BYTE_SEX;
static int i860_ip(
char *str);
static void md_insn_to_chars(
unsigned char *buf,
long val,
int n);
const relax_typeS md_relax_table[] = { 0 };
/* handle of the OPCODE hash table */
static struct hash_control *op_hash = NULL;
static void s_dual(
int mode);
static void s_i860_align(
int value);
static void s_i860_org(
int value);
const pseudo_typeS md_pseudo_table[] = {
{ "float", float_cons, 'f' },
{ "int", cons, 4 },
{ "align", s_i860_align, 0 }, /* Alignment is in bytes */
{ "blkb", s_space, 0 }, /* Reserve space, in bytes */
{ "dual", s_dual, 1 }, /* Dual insn mode crock */
{ "enddual",s_dual, 0 },
{ "extern", s_globl, 0 }, /* as860 equiv of .globl */
{ "ln", s_line, 0 }, /* as860 equiv of .line */
{ "org", s_i860_org, 0 },
#ifndef NeXT
{ "quad", big_cons, 16 }, /* A quad is 16 bytes on 860 */
#endif /* NeXT */
{ "string", stringer, 1 }, /* as860 equiv of .asciz */
{ NULL, 0, 0 },
};
static int dual_insn_mode = 0;
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful */
const char md_comment_chars[] = "|!";
/* This array holds the chars that only start a comment at the beginning of
a line. If the line seems to have the form '# 123 filename'
.line and .file directives will appear in the pre-processed output */
/* Note that input_file.c hand checks for '#' at the beginning of the
first line of the input file. This is because the compiler outputs
#NO_APP at the beginning of its output. */
/* Also note that a '/' followed by a '*' will always start a comment */
const char md_line_comment_chars[] = "#";
/* Chars that can be used to separate mant from exp in floating point nums */
const char md_EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant */
/* As in 0f12.456 */
/* or 0d1.2345e12 */
const char md_FLT_CHARS[] = "rRsSfFdDxXpP";
/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
changed in read.c . Ideally it shouldn't have to know about it at all,
but nothing is ideal around here.
*/
int size_reloc_info = sizeof(struct relocation_info);
static unsigned char octal[256];
#define isoctal(c) octal[c]
static unsigned char toHex[256];
/* Local fatal error flag. Used to bomb assembler in md_end after scanning input */
static int I860_errors;
static int insn_count; /* Track insns assembled, as a word count */
struct i860_it {
char *error;
unsigned long opcode;
struct nlist *nlistp;
expressionS exp;
int pcrel;
enum reloc_type_i860 reloc;
};
static struct i860_it the_insn;
#ifdef I860_DEBUG
static void print_insn(
struct i860_it *insn);
#endif /* I860_DEBUG */
static int getExpression(
char *str);
static char *expr_end;
/* Flags returned by i860_ip() */
#define INSERT_NOP 0x00000001
static
void
s_dual(
int mode)
{
dual_insn_mode = mode;
}
static
void
s_i860_align(
int value)
{
register unsigned int temp;
register long int temp_fill;
unsigned int i = 0;
unsigned int bytes;
char *toP;
bytes = temp = get_absolute_expression ();
#define MAX_ALIGNMENT (1 << 15)
if ( temp > MAX_ALIGNMENT ) {
as_warn("Alignment too large: %d. assumed.", temp = MAX_ALIGNMENT);
}
/*
* For the i860, `.align (1<<n)' actually means `.align n'
* so we have to convert it.
*/
if (temp != 0) {
for (i = 0; (temp & 1) == 0; temp >>= 1, ++i)
;
}
if (temp != 1) {
as_warn("Alignment not a power of 2");
}
temp = i;
if (*input_line_pointer == ',') {
input_line_pointer ++;
temp_fill = get_absolute_expression ();
} else {
if ( frchain_now->frch_nsect == text_nsect )
temp_fill = OP_NOP;
else
temp_fill = 0;
}
if ( frchain_now->frch_nsect == text_nsect ) /* emit NOPs! */
{ /* Grow the code frag as needed and dump nops into it. */
if ( bytes & 3 )
as_warn( "Instruction alignment must be a multiple of 4." );
bytes &= ~3;
/* This is really tacky, but works for a fixed width insn machine */
while ( bytes && ((insn_count * 4) % bytes) != 0 )
{
toP = frag_more(4); /* Add an instruction */
/* put out the opcode */
md_insn_to_chars(toP, temp_fill, 4); /* Fill instruction */
insn_count++;
}
/* Clean up */
demand_empty_rest_of_line();
return;
}
/* Only make a frag if we HAVE to. . . */
if (temp) {
frag_align (temp, (int)temp_fill);
}
/*
* If this alignment is larger than any previous alignment then this
* becomes the section's alignment.
*/
if(frchain_now->frch_section.align < temp)
frchain_now->frch_section.align = temp;
demand_empty_rest_of_line();
return;
}
static
void
s_i860_org(
int value)
{
register segT segment;
expressionS exp;
register long int temp_fill;
register char *p;
extern segT get_known_segmented_expression();
/*
* Don't believe the documentation of BSD 4.2 AS.
* There is no such thing as a sub-segment-relative origin.
* Any absolute origin is given a warning, then assumed to be segment-relative.
* Any segmented origin expression ("foo+42") had better be in the right
* segment or the .org is ignored.
*
* BSD 4.2 AS warns if you try to .org backwards. We cannot because we
* never know sub-segment sizes when we are reading code.
* BSD will crash trying to emit -ve numbers of filler bytes in certain
* .orgs. We don't crash, but see as-write for that code.
*/
segment = get_known_segmented_expression(& exp);
if ( *input_line_pointer == ',' ) {
input_line_pointer ++;
temp_fill = get_absolute_expression ();
} else
temp_fill = 0;
if((segment != SEG_SECT ||
exp.X_add_symbol->sy_other != frchain_now->frch_nsect) &&
segment != SEG_ABSOLUTE)
as_warn("Illegal expression. current section assumed.");
if ( exp.X_add_symbol != NULL )
as_warn("Symbol relative .org may corrupt alignment.");
else if ( exp.X_add_number & 3 )
{
exp.X_add_number &= ~3;
as_warn(".org not on instruction boundry. Adjusted to \".org %ld\"",
exp.X_add_number);
}
if ( exp.X_add_symbol == NULL )
insn_count = exp.X_add_number >> 2;
p = frag_var (rs_org, 1, 1, (relax_substateT)0, exp . X_add_symbol,
exp . X_add_number, (char *)0);
* p = temp_fill;
demand_empty_rest_of_line();
}
/*
* This function is called once, at assembler startup time. This should
* set up all the tables, etc that the MD part of the assembler needs
*/
void
md_begin(
void)
{
register char *retval = NULL;
register int i;
int j = 0;
insn_count = 0;
if ((op_hash = hash_new()) == NULL)
as_fatal("Virtual memory exhausted");
for (i = 0; i < NUMOPCODES; ++i) {
if (~i860_opcodes[i].mask & i860_opcodes[i].match) {
printf("bad opcode - `%s %s'\n",
i860_opcodes[i].name, i860_opcodes[i].args);
++j;
}
}
if (j)
exit(1);
for (i = 0; i < NUMOPCODES; ++i) {
retval = hash_insert(op_hash, (char *)i860_opcodes[i].name,
(char *)&i860_opcodes[i]);
if(retval && *retval) {
as_fatal("Internal Error: Can't hash %s: %s",
i860_opcodes[i].name, retval);
}
while (!i860_opcodes[i].last)
++i;
}
for (i = '0'; i < '8'; ++i)
octal[i] = 1;
for (i = '0'; i <= '9'; ++i)
toHex[i] = i - '0';
for (i = 'a'; i <= 'f'; ++i)
toHex[i] = i + 10 - 'a';
for (i = 'A'; i <= 'F'; ++i)
toHex[i] = i + 10 - 'A';
I860_errors = 0;
return;
}
void
md_end(
void)
{
if ( I860_errors )
{
fprintf( stderr, "%d fatal %s encountered during assembly.\n", I860_errors,
(I860_errors == 1 ? "error" : "errors") );
exit( 42 ); /* Fatal errors seen during assembly */
}
return;
}
void
md_assemble(
char *str)
{
char *toP;
int flags;
assert(str);
flags = i860_ip(str);
if ( flags & INSERT_NOP )
{
toP = frag_more(4);
/* put out the opcode */
md_insn_to_chars(toP, OP_NOP, 4);
++insn_count;
}
toP = frag_more(4);
/* put out the opcode */
md_insn_to_chars(toP, the_insn.opcode, 4);
++insn_count;
/* put out the symbol-dependent stuff */
if (the_insn.reloc != NO_RELOC) {
fix_new(
frag_now, /* which frag */
(toP - frag_now->fr_literal), /* where */
4, /* size */
the_insn.exp.X_add_symbol,
the_insn.exp.X_subtract_symbol,
the_insn.exp.X_add_number,
the_insn.pcrel, 0,
the_insn.reloc
);
}
}
static
int
i860_ip(
char *str)
{
char *s;
char *op;
const char *args;
char c;
struct i860_opcode *insn;
char *argsStart;
char *s1;
unsigned long opcode;
unsigned int mask;
int this_insn_is_dual = 0;
int adjustment;
int align_mask;
int match = FALSE;
int comma = 0;
int flags = 0;
static int expect_int_insn; /* Tracking for fp/int insns in dual mode. */
/* Advance s to end of opcode */
for (s = str; islower(*s) || *s == '.' || isdigit(*s); ++s)
;
switch (*s) {
case '\0':
break;
case ',':
comma = 1;
/*FALLTHROUGH*/
case ' ':
case '\t':
*s++ = '\0';
break;
default:
as_warn("Unknown opcode: `%s'", str);
exit(1);
}
/* Code to sniff for 'd.' prefix here and flag for dual insn mode */
op = str;
if ( *op == 'd' && *(op + 1) == '.' )
{
op += 2;
this_insn_is_dual = 1;
}
if ((insn = (struct i860_opcode *) hash_find(op_hash, op)) == NULL) {
as_warn("Unknown instruction or format: `%s'.", str);
memset(&the_insn, '\0', sizeof(the_insn)); /* Patch in no-op to hold alignment */
the_insn.reloc = NO_RELOC;
the_insn.opcode = OP_NOP;
++I860_errors; /* Flag as fatal error */
return flags;
}
if (comma) {
*--s = ',';
}
argsStart = s;
for (;;) {
opcode = insn->match;
memset(&the_insn, '\0', sizeof(the_insn));
the_insn.reloc = NO_RELOC;
/*
* Build the opcode, checking as we go to make
* sure that the operands match
*/
for (args = insn->args; ; ++args) {
align_mask = 0;
switch (*args) {
case '\0': /* end of args */
if (*s == '\0') {
match = TRUE;
}
break;
case '+':
case '(': /* these must match exactly */
case ')':
case ',':
case ' ':
if (*s++ == *args)
continue;
break;
case 'C': /* Control register */
if (strncmp(s, "fir", 3) == 0) {
s += 3;
SET_RS2(opcode, 0);
continue;
}
if (strncmp(s, "psr", 3) == 0) {
s += 3;
SET_RS2(opcode, 1);
continue;
}
if (strncmp(s, "dirbase", 7) == 0) {
s += 7;
SET_RS2(opcode, 2);
continue;
}
if (strncmp(s, "db", 2) == 0) {
s += 2;
SET_RS2(opcode, 3);
continue;
}
if (strncmp(s, "fsr", 3) == 0) {
s += 3;
SET_RS2(opcode, 4);
continue;
}
if (strncmp(s, "epsr", 4) == 0) {
s += 4;
SET_RS2(opcode, 5);
continue;
}
break;
case '1': /* next operand must be a register */
case '2':
case 'd':
{
switch (c = *s++) {
case 'f': /* frame pointer */
if (*s++ == 'p') {
mask = 3; /* register fp is alias for r3 */
break;
}
goto error;
case 's': /* global register */
if (*s++ == 'p') {
mask = 2; /* register sp is alias for r2 */
break;
}
goto error;
case 'r': /* any register */
if (!isdigit(c = *s++)) {
goto error;
}
if (isdigit(*s)) {
if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) {
goto error;
}
} else {
c -= '0';
}
mask= c;
break;
default:
goto error;
}
/*
* Got the register, now figure out where
* it goes in the opcode.
*/
switch (*args) {
case '1':
SET_RS1(opcode, mask);
continue;
case '2':
SET_RS2(opcode, mask);
continue;
case 'd':
SET_RD(opcode, mask);
continue;
}
}
break;
case 'e': /* next operand is a floating point register */
case 'f':
case 'g':
case 'E':
case 'F':
case 'G':
case 'H':
if (*s++ == 'f' && isdigit(*s)) {
mask = *s++;
if (isdigit(*s)) {
mask = 10 * (mask - '0') + (*s++ - '0');
if (mask >= 32) {
break;
}
} else {
mask -= '0';
}
if ( (*args == 'E' || *args == 'F' || *args == 'G') && (mask & 1) )
{
as_warn( "f%d: Even register required. Adjusted to f%d.",
mask, mask & 0x1E );
mask &= 0x1E;
}
else if ( *args == 'H' && (mask & 3) )
{
as_warn( "f%d: Quad register required. Adjusted to f%d.",
mask, mask & 0x1C );
mask &= 0x1C;
}
switch (*args) {
case 'e':
case 'E':
SET_RS1(opcode, mask);
continue;
case 'f':
case 'F':
SET_RS2(opcode, mask);
continue;
case 'g':
case 'G':
case 'H':
SET_RD(opcode, mask);
continue;
}
}
break;
case 'B': /* 5 bit immediate unsigned constant */
(void)getExpression(s);
s = expr_end;
if ( the_insn.exp.X_seg != SEG_ABSOLUTE )
{
as_warn( "Constant expression expected" );
++I860_errors;
}
if ( the_insn.exp.X_add_number < 0 || the_insn.exp.X_add_number > 31 )
as_warn( "Constant must be between 0 and 31. Modulo 32 applied." );
SET_RS1(opcode, the_insn.exp.X_add_number); /* Takes const modulo 32 */
continue;
case 'D': /* immediate unsigned constant used in shift opcodes */
(void)getExpression(s);
s = expr_end;
if ( the_insn.exp.X_seg != SEG_ABSOLUTE )
{
as_warn( "Constant expression expected" );
++I860_errors;
}
if ( the_insn.exp.X_add_number < 0 || the_insn.exp.X_add_number > 31 )
as_warn( "Constant must be between 0 and 31. Modulo 32 applied." );
opcode |= (the_insn.exp.X_add_number & 0x1F);
continue;
case 'i': /* low 16 bits, byte aligned */
the_insn.reloc = I860_RELOC_LOW0;
goto immediate;
case 'I': /* high 16 bits */
the_insn.reloc = I860_RELOC_HIGH;
goto immediate;
case 'j': /* low 16 bits, short aligned */
the_insn.reloc = I860_RELOC_LOW1;
align_mask = 1;
goto immediate;
case 'k': /* low 16 bits, int aligned */
the_insn.reloc = I860_RELOC_LOW2;
align_mask = 3;
goto immediate;
case 'l': /* low 16 bits, double aligned */
the_insn.reloc = I860_RELOC_LOW3;
align_mask = 7;
goto immediate;
case 'm': /* low 16 bits, quad aligned */
the_insn.reloc = I860_RELOC_LOW4;
align_mask = 15;
goto immediate;
case 'n': /* low 16 bits, byte aligned, split field */
the_insn.reloc = I860_RELOC_SPLIT0;
goto immediate;
case 'o': /* low 16 bits, short aligned, split field */
the_insn.reloc = I860_RELOC_SPLIT1;
align_mask = 1;
goto immediate;
case 'p': /* low 16 bits, int aligned, split field */
the_insn.reloc = I860_RELOC_SPLIT2;
align_mask = 3;
goto immediate;
case 'J': /* High 16 bits, requiring adjustment */
the_insn.reloc = I860_RELOC_HIGHADJ;
goto immediate;
case 'K': /* 26 bit PC relative immediate */
the_insn.reloc = I860_RELOC_BRADDR;
the_insn.pcrel = 1;
goto immediate;
case 'L': /* 16 bit PC relative split format immediate */
the_insn.reloc = I860_RELOC_SPLIT0;
the_insn.pcrel = 1;
goto immediate;
/*FALLTHROUGH*/
immediate:
if(*s==' ')
s++;
adjustment = 0;
if ( *s == 'h' && *(s + 1) == '%' )
{
adjustment = I860_RELOC_HIGH;
if ( the_insn.reloc == I860_RELOC_LOW0 && the_insn.pcrel == 0 )
{
the_insn.reloc = I860_RELOC_HIGH;
}
else
as_warn("Improper use of h%%.");
s += 2;
}
else if ( *s == 'h' && *(s + 1) == 'a' && *(s + 2) == '%' )
{
adjustment = I860_RELOC_HIGHADJ;
if ( the_insn.reloc == I860_RELOC_LOW0 && the_insn.pcrel == 0 )
{
the_insn.reloc = I860_RELOC_HIGHADJ;
}
else
as_warn("Improper use of ha%%.");
s += 3;
}
else if ( *s == 'l' && *(s + 1) == '%' )
{ /* the_insn.reloc is correct as is. */
adjustment = I860_RELOC_LOW0;
s += 2;
}
/* Note that if the getExpression() fails, we will still have
created U entries in the symbol table for the 'symbols'
in the input string. Try not to create U symbols for
registers, etc. */
/* This stuff checks to see if the expression ends
in '(', as in ld.l foo(r20). If it does, it
removes the '(' from the expression, and
re-sets 's' to point to the right place */
for(s1=s;*s1 && *s1!=','&& *s1!=')';s1++)
;
if( s1 != s && *s1 == '(' && s1[1] == 'r' && isdigit(s1[2])
&& (s1[3]==')' || (isdigit(s1[3]) && s1[4] == ')')) ) {
*s1='\0';
(void)getExpression(s);
*s1='(';
s=s1;
}
else
{
(void)getExpression(s);
s = expr_end;
}
/*
* If there is an adjustment, we assume the user knows what
* they are doing. If no adjustment, we very carefully range
* check for both signed and unsigned operations, to avoid
* unpleasant suprises. The checks are skipped for branch and
* call instructions, matching the behavior of the Intel assembler.
*/
if ( ! adjustment && *op != 'b' && *op != 'c' )
{
if ( the_insn.exp.X_seg != SEG_ABSOLUTE )
{
as_warn(
"Non-absolute expression requires h%%, l%%, or ha%% prefix."
);
}
else
{
if ( IS_LOGOP(opcode) )
{
if ( ((unsigned)the_insn.exp.X_add_number) > 0xFFFF )
as_warn("%lu is too big for 16 bit unsigned value!",
the_insn.exp.X_add_number);
}
else
{
if ( ((int)the_insn.exp.X_add_number) > 32767 ||
((int)the_insn.exp.X_add_number) < -32768 )
as_warn("%ld is out of range for 16 bit signed value!",
the_insn.exp.X_add_number);
if ((align_mask & the_insn.exp.X_add_number) != 0)
as_warn("Const offset 0x%x incorrectly aligned.",
(unsigned int)the_insn.exp.X_add_number);
}
}
}
continue;
default:
abort();
}
break;
}
error:
if (match == FALSE) {
/* args don't match */
if (!insn->last) {
++insn;
s = argsStart;
continue;
} else {
as_warn("Illegal operands (%s %s).", str, argsStart);
++I860_errors;
return flags;
}
}
break;
}
/* If the last insn was dual, check and make sure that this insn is integer insn */
if ( expect_int_insn && (dual_insn_mode || this_insn_is_dual) )
{
if ( (opcode & OP_PREFIX_MASK) == PREFIX_FPU || opcode == OP_FNOP )
{
as_warn( "Core half of prev dual insn pair missing." );
}
expect_int_insn = 0;
}
/* Check the insn format and fold in the dual mode bit if appropriate. */
if ( dual_insn_mode || this_insn_is_dual )
{
if ( (opcode & OP_PREFIX_MASK) == PREFIX_FPU || opcode == OP_FNOP )
{
if ( insn_count & 1 ) /* Odd insn, not on 64 bit bound! */
{
as_warn( "Dual FP insn on odd addr." );
}
opcode |= DUAL_INSN_MODE_BIT;
expect_int_insn = 1;
}
else if ( this_insn_is_dual ) /* d. prefix on a non-FPU insn error */
{
as_warn("d. prefix not allowed for `%s'. (Ignored!)", op);
}
}
else
expect_int_insn = 0; /* Single insn mode. */
/* Check for correct alignment of const in branch to label + offset */
if ( (the_insn.reloc == I860_RELOC_BRADDR
|| (the_insn.pcrel && the_insn.reloc == I860_RELOC_SPLIT0)
) && (the_insn.exp.X_add_number & 3) )
as_warn( "Branch offset is not aligned to instruction boundry!" );
the_insn.opcode = opcode;
return flags;
}
static
int
getExpression(
char *str)
{
char *save_in;
segT seg;
save_in = input_line_pointer;
input_line_pointer = str;
switch (seg = expression(&the_insn.exp)) {
case SEG_ABSOLUTE:
case SEG_SECT:
case SEG_DIFFSECT:
case SEG_UNKNOWN:
case SEG_BIG:
case SEG_NONE:
break;
default:
the_insn.error = "bad segment";
expr_end = input_line_pointer;
input_line_pointer=save_in;
return 1;
}
expr_end = input_line_pointer;
input_line_pointer = save_in;
return 0;
}
#define MAX_LITTLENUMS 6
/*
This is identical to the md_atof in m68k.c. I think this is right,
but I'm not sure.
Turn a string in input_line_pointer into a floating point constant of type
type, and store the appropriate bytes in *litP. The number of LITTLENUMS
emitted is stored in *sizeP . An error message is returned, or NULL on OK.
*/
char *
md_atof(
int type,
char *litP,
int *sizeP)
{
int prec;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
LITTLENUM_TYPE *wordP;
char *t;
char *atof_ieee();
switch(type) {
case 'f':
case 'F':
case 's':
case 'S':
prec = 2;
break;
case 'd':
case 'D':
case 'r':
case 'R':
prec = 4;
break;
/* The following two formats get reduced to doubles. */
case 'x':
case 'X':
type = 'd';
prec = 4;
break;
case 'p':
case 'P':
type = 'd';
prec = 4;
break;
default:
*sizeP=0;
return "Bad call to MD_ATOF()";
}
t=atof_ieee(input_line_pointer,type,words);
if(t)
input_line_pointer=t;
*sizeP=prec * sizeof(LITTLENUM_TYPE);
for(wordP=words;prec--;) {
md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE));
litP+=sizeof(LITTLENUM_TYPE);
}
return ""; /* Someone should teach Dean about null pointers */
}
/*
* Write out big-endian. Valid for data only in our I860 implementation.
*/
void
md_number_to_chars(
char *buf,
long val,
int n)
{
switch(n) {
case 4:
*buf++ = val >> 24;
*buf++ = val >> 16;
case 2:
*buf++ = val >> 8;
case 1:
*buf = val;
break;
default:
abort();
}
return;
}
#ifdef BYTE_SWAP
/*
* Write out little-endian. Valid for instructions only in
* our i860 implementation.
*/
static
void
md_insn_to_chars(
unsigned char *buf,
long val,
int n)
{
switch(n) {
case 4:
*buf++ = val;
*buf++ = val >> 8;
*buf++ = val >> 16;
*buf++ = val >> 24;
break;
case 2:
*buf++ = val;
*buf++ = val >> 8;
break;
case 1:
*buf = val;
break;
default:
abort();
}
return;
}
#else /* !defined(BYTE_SWAP) */
static
void
md_insn_to_chars(
unsigned char *buf,
long val,
int n)
{
md_number_to_chars(buf,val,n);
}
#endif /* BYTE_SWAP */
void
md_number_to_imm(
unsigned char *buf,
long val,
int n,
fixS *fixP,
int nsect)
{
unsigned long opcode;
if ( nsect == text_nsect && (n % 4) != 0 )
as_warn("Immediate write of non-aligned data into text segment." );
if (nsect != text_nsect ||
fixP->fx_r_type == NO_RELOC ||
fixP->fx_r_type == I860_RELOC_VANILLA)
{
switch (n) { /* Write out the data big-endian style. */
case 1:
*buf = val;
break;
case 2:
*buf++ = (val>>8);
*buf = val;
break;
case 4:
*buf++ = (val>>24);
*buf++ = (val>>16);
*buf++ = (val>>8);
*buf = val;
break;
default:
abort();
}
return;
}
assert(n == 4); /* Better be an instruction with relocation data.... */
assert(fixP->fx_r_type < NO_RELOC && fixP->fx_r_type > I860_RELOC_VANILLA);
/*
* Here is where we do initial bit fiddling to load immediate
* values into the i860 bit fields.
*/
#ifdef BYTE_SWAP
/* Note that all of these insns are ultimately little-endian */
/* Get the opcode from the buffer. Less efficient, but more coherent... */
opcode = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
#else
opcode = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
#endif
/* Apply the relocation value 'val' */
switch (fixP->fx_r_type) {
case I860_RELOC_PAIR:
as_warn("questionable relocation type I860_RELOC_PAIR");
break;
case I860_RELOC_HIGH:
opcode &= ~0xFFFF;
opcode |= ((val >> 16) & 0xFFFF);
break;
case I860_RELOC_LOW0:
opcode &= ~0xFFFF;
opcode |= (val & 0xFFFF);
break;
case I860_RELOC_LOW1:
opcode &= 0xFFFF0001;
opcode |= (val & 0xFFFE); /* Bit 0 is an insn bit! */
break;
case I860_RELOC_LOW2:
opcode &= 0xFFFF0003;
opcode |= (val & 0xFFFC); /* Bits 0 and 1 are insn bits! */
break;
case I860_RELOC_LOW3:
opcode &= 0xFFFF0007;
opcode |= (val & 0xFFF8); /* Bits 0 thru 2 are insn bits! */
break;
case I860_RELOC_LOW4:
opcode &= 0xFFFF000F;
opcode |= (val & 0xFFF0); /* Bits 0 thru 3 are insn bits! */
break;
case I860_RELOC_SPLIT0:
opcode &= 0xFFE0F800;
if ( fixP->fx_pcrel ) /* A 16 bit branch relative insn? */
val >>= 2; /* Convert to word address */
opcode |= ((val & 0xF800) << 5) | (val & 0x7FF);
break;
case I860_RELOC_SPLIT1:
opcode &= 0xFFE0F801; /* Again, bit 0 is an insn bit! */
opcode |= ((val & 0xF800) << 5) | (val & 0x7FE);
break;
case I860_RELOC_SPLIT2:
opcode &= 0xFFE0F803; /* Bits 0 and 1 are insn bits! */
opcode |= ((val & 0xF800) << 5) | (val & 0x7FC);
break;
case I860_RELOC_HIGHADJ: /* Adjusted variant */
opcode &= ~0xFFFF;
/* If the low half would be negative, compensate by adding 1 to
* high half.
*/
if ( (val & 0x8000) != 0 )
val = (val >> 16) + 1;
else
val = (val >> 16);
opcode |= (val & 0xFFFF);
break;
case I860_RELOC_BRADDR:
if ( fixP->fx_pcrel ) /* A 26 bit branch relative insn? */
val >>= 2; /* Convert to word address */
opcode &= 0xFC000000;
opcode |= (val & 0x03FFFFFF);
break;
default:
as_warn("bad relocation type: 0x%02x", fixP->fx_r_type);
break;
}
#ifdef BYTE_SWAP
buf[0] = opcode;
buf[1] = opcode >> 8;
buf[2] = opcode >> 16;
buf[3] = opcode >> 24;
#else
buf[3] = opcode;
buf[2] = opcode >> 8;
buf[1] = opcode >> 16;
buf[0] = opcode >> 24;
#endif
return;
}
/* should never be called for i860 */
void
md_convert_frag(
fragS *fragP)
{
fprintf(stderr, "i860_convert_frag\n");
abort();
}
/* should never be called for i860 */
int
md_estimate_size_before_relax(
fragS *fragP,
int nsect)
{
fprintf(stderr, "i860_estimate_size_before_relax\n");
abort();
return 0;
}
#ifdef I860_DEBUG
/* for debugging only */
static void
print_insn(
struct i860_it *insn)
{
char *Reloc[] = {
"RELOC_8",
"RELOC_16",
"RELOC_32",
"RELOC_DISP8",
"RELOC_DISP16",
"RELOC_DISP32",
"RELOC_WDISP30",
"RELOC_WDISP22",
"RELOC_HI22",
"RELOC_22",
"RELOC_13",
"RELOC_LO10",
"RELOC_SFA_BASE",
"RELOC_SFA_OFF13",
"RELOC_BASE10",
"RELOC_BASE13",
"RELOC_BASE22",
"RELOC_PC10",
"RELOC_PC22",
"RELOC_JMP_TBL",
"RELOC_SEGOFF16",
"RELOC_GLOB_DAT",
"RELOC_JMP_SLOT",
"RELOC_RELATIVE",
"NO_RELOC"
};
if (insn->error) {
fprintf(stderr, "ERROR: %s\n");
}
fprintf(stderr, "opcode=0x%08x\n", insn->opcode);
fprintf(stderr, "reloc = %s\n", Reloc[insn->reloc]);
fprintf(stderr, "exp = {\n");
fprintf(stderr, "\t\tX_add_symbol = %s\n",
insn->exp.X_add_symbol ?
(insn->exp.X_add_symbol->sy_name ?
insn->exp.X_add_symbol->sy_name : "???") : "0");
fprintf(stderr, "\t\tX_sub_symbol = %s\n",
insn->exp.X_subtract_symbol ?
(insn->exp.X_subtract_symbol->sy_name ?
insn->exp.X_subtract_symbol->sy_name : "???") : "0");
fprintf(stderr, "\t\tX_add_number = %d\n",
insn->exp.X_add_number);
fprintf(stderr, "}\n");
return;
}
#endif /* I860_DEBUG */
int
md_parse_option(
char **argP,
int *cntP,
char ***vecP)
{
return 1;
}